/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is confidential property of Nordic
 * Semiconductor ASA.Terms and conditions of usage are described in detail
 * in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */

/** @file
* @brief Radio Test example. 
*
* @defgroup nrf_radio_test_example Radio Test example
* @{
* @ingroup nrf_examples_nrf6310
*
* @brief Example project on testing the radio
*
* This example shows how to configure the radio for constant RX/TX carrier,
* modulated TX carrier and RX/TX sweep. The tests are controlled with  simple
* one-letter commands via the serial port at 38400 baud. These radio parameters
* can be set at any time during the tests: output power, bit rate and channel.
* The time on each channel in sweep mode can also be set in steps of 1 ms
* from 1 ms to 99 ms.
*
*/
#include <stdint.h>
#include <stdbool.h>
#include "nrf.h"
#include "radio_test.h"
#include "simple_uart.h"

static uint8_t mode_            = RADIO_MODE_MODE_Nrf_2Mbit;
static uint8_t txpower_         = RADIO_TXPOWER_TXPOWER_0dBm;
static uint8_t channel_start_   = 0;
static uint8_t channel_end_     = 80;
static uint8_t delayms_         = 10;

#define RX_PIN_NUMBER  3
#define TX_PIN_NUMBER  1

static bool sweep = false;

typedef enum
{
  RADIO_TEST_NOP,               /*!< No test running      */
  RADIO_TEST_TXCC,              /*!< TX constant carrier  */
  RADIO_TEST_TXMC,              /*!< TX modulated carrier */
  RADIO_TEST_TXSWEEP,           /*!< TX sweep             */
  RADIO_TEST_RXC,               /*!< RX constant carrier  */
  RADIO_TEST_RXSWEEP,           /*!< RX sweep             */
} radio_tests_t;

#define BELL 7 // Bell
#define BS   8 // Backspace


/*
 * Turn on all peripherals used in this example and wait for them to power up.
*/
static void init(void)
{
  // Turn peripherals on
  NRF_POWER->PERPOWER |= (POWER_PERPOWER_PPI_Msk | POWER_PERPOWER_RADIO_Msk | POWER_PERPOWER_UART0_Msk | POWER_PERPOWER_TIMER0_Msk | POWER_PERPOWER_RNG_Msk);
  // Wait for the peripherals to power up:
  while((NRF_POWER->PERRDY & POWER_PERPOWER_PPI_Msk) == 0)
  {
  }
  while((NRF_POWER->PERRDY & POWER_PERPOWER_RADIO_Msk) == 0)
  {
  }
  while((NRF_POWER->PERRDY & POWER_PERPOWER_UART0_Msk) == 0)
  {
  }
  while((NRF_POWER->PERRDY & POWER_PERPOWER_TIMER0_Msk) == 0)
  {
  }
  while((NRF_POWER->PERRDY & POWER_PERPOWER_RNG_Msk) == 0)
  {
  }
  NRF_RNG->TASKS_START = 1;
  
  /* Start 16 MHz crystal oscillator */
  NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
  NRF_CLOCK->TASKS_HFCLKSTART = 1;

  /* Wait for the external oscillator to start up */
  while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) 
  {
  }  
}

/*
* Output usage info to the serial port.
*/
static void help(void)
{
  simple_uart_putstring((const uint8_t *)"Usage:\r\n");
  simple_uart_putstring((const uint8_t *)"a: Enter start channel for sweep/channel for constant carrier\r\n");
  simple_uart_putstring((const uint8_t *)"b: Enter end channel for sweep\r\n");
  simple_uart_putstring((const uint8_t *)"c: Start TX carrier\r\n");
  simple_uart_putstring((const uint8_t *)"d: Enter time on each channel (1ms-99ms)\r\n");
  simple_uart_putstring((const uint8_t *)"e: Cancel sweep/carrier\r\n");
  simple_uart_putstring((const uint8_t *)"m: Enter data rate\r\n");
  simple_uart_putstring((const uint8_t *)"o: Start modulated TX carrier\r\n");
  simple_uart_putstring((const uint8_t *)"p: Enter output power\r\n");
  simple_uart_putstring((const uint8_t *)"s: Print current delay, channels and so on\r\n");
  simple_uart_putstring((const uint8_t *)"r: Start RX sweep\r\n");
  simple_uart_putstring((const uint8_t *)"t: Start TX sweep\r\n");
  simple_uart_putstring((const uint8_t *)"x: Start RX carrier\r\n");
}


/*
* Read two-digit decimal number from the serial port. Backspace is supported on the first char.
*/
static uint8_t get_dec2(void)
{
  uint8_t buf[2], i, c;
  i = 0;
  buf[0] = buf[1] = 0;
  while(i < 2)
  {
    c = simple_uart_get();
    if ((i > 0) && (c == BS))
    {
      simple_uart_put(c);
      i--;
    }
    else if ((c >= '0') && (c <= '9'))
    {
      simple_uart_put(c);
      buf[i] = c - '0';
      i++;
    }
    else
    {
      simple_uart_put(BELL);
    }
  }
  simple_uart_putstring((const uint8_t *)"\r\n");
  return buf[0] * 10 + buf[1];
}

/*
* Read data rate 
*/
void get_datarate(void)
{
  uint8_t c;

  simple_uart_putstring((const uint8_t *)"Enter data rate ('0'=250 Kbit/s, '1'=1 Mbit/s and '2'=2 Mbit/s):");
  while(true)
  {
    c = simple_uart_get();
    if ((c >= '0') && (c <= '2'))
    {
      simple_uart_put(c);
      break;
    }
    else
    {
      simple_uart_put(BELL);
    }
  }
  if (c == '0')
  {
    mode_ = RADIO_MODE_MODE_Nrf_250Kbit;
  }
  else if (c == '1')
  {
    mode_ = RADIO_MODE_MODE_Nrf_1Mbit;
  }
  else
  {
    mode_ = RADIO_MODE_MODE_Nrf_2Mbit;
  }
  simple_uart_putstring((const uint8_t *)"\r\n");
}

/*
* Read output power.
*/
void get_power(void)
{
  uint8_t c;

  simple_uart_putstring((const uint8_t *)"Enter output power ('0'=+4 dBm, '1'=0 dBm,...,'7'=-40 dBm):");
  while(true)
  {
    c = simple_uart_get();
    if ((c >= '0') && (c <= '7'))
    {
      simple_uart_put(c);
      break;
    }
    else
    {
      simple_uart_put(BELL);
    }
  }
  switch(c)
  {
    case '0':
      txpower_ =  RADIO_TXPOWER_TXPOWER_Pos4dBm;
      break;
    case '1':
      txpower_ =  RADIO_TXPOWER_TXPOWER_0dBm;
      break;
    case '2':
      txpower_ = RADIO_TXPOWER_TXPOWER_Neg4dBm;
      break;
    case '3':
      txpower_ = RADIO_TXPOWER_TXPOWER_Neg8dBm;
      break;
    case '4':
      txpower_ = RADIO_TXPOWER_TXPOWER_Neg12dBm;
      break;
    case '5':
      txpower_ = RADIO_TXPOWER_TXPOWER_Neg16dBm;
      break;
    case '6':
      txpower_ = RADIO_TXPOWER_TXPOWER_Neg20dBm;
      break;
    case '7':
    default:
      txpower_ = RADIO_TXPOWER_TXPOWER_Neg40dBm;
      break;
  }
  simple_uart_putstring((const uint8_t *)"\r\n");
}

/*
* print two-gigit decimal number.
*/
static void print_dec2(uint8_t val)
{
  simple_uart_put(val / 10 + '0');
  simple_uart_put(val % 10 + '0');
}

/*
* Output current parameters to the serial port.
*/
void print_parameters(void)
{
  simple_uart_putstring((const uint8_t *)"Parameters:\r\n");
  switch(mode_)
  {
    case RADIO_MODE_MODE_Nrf_250Kbit:
      simple_uart_putstring((const uint8_t *)"Data rate...........: 250 Kbit/s\r\n");
      break;
    case RADIO_MODE_MODE_Nrf_1Mbit:
      simple_uart_putstring((const uint8_t *)"Data rate...........: 1 Mbit/s\r\n");
      break;
    case RADIO_MODE_MODE_Nrf_2Mbit:
      simple_uart_putstring((const uint8_t *)"Data rate...........: 2 Mbit/s\r\n");
      break;
  }
  switch(txpower_)
  {
    case RADIO_TXPOWER_TXPOWER_Pos4dBm:
      simple_uart_putstring((const uint8_t *)"TX Power............: +4 dBm\r\n");
      break;
    case RADIO_TXPOWER_TXPOWER_0dBm:
      simple_uart_putstring((const uint8_t *)"TX Power............: 0 dBm\r\n");
      break;
    case RADIO_TXPOWER_TXPOWER_Neg4dBm:
      simple_uart_putstring((const uint8_t *)"TX Power............: -4 dBm\r\n");
      break;
    case RADIO_TXPOWER_TXPOWER_Neg8dBm:
      simple_uart_putstring((const uint8_t *)"TX Power............: -8 dBm\r\n");
      break;
    case RADIO_TXPOWER_TXPOWER_Neg12dBm:
      simple_uart_putstring((const uint8_t *)"TX Power............: -12 dBm\r\n");
      break;
    case RADIO_TXPOWER_TXPOWER_Neg16dBm:
      simple_uart_putstring((const uint8_t *)"TX Power............: -16 dBm\r\n");
      break;
    case RADIO_TXPOWER_TXPOWER_Neg20dBm:
      simple_uart_putstring((const uint8_t *)"TX Power............: -20 dBm\r\n");
      break;
    case RADIO_TXPOWER_TXPOWER_Neg40dBm:
      simple_uart_putstring((const uint8_t *)"TX Power............: -40 dBm\r\n");
      break;
  }
  simple_uart_putstring((const uint8_t *)"(Start) Channel.....: ");
  print_dec2(channel_start_);
  simple_uart_putstring((const uint8_t *)"\r\nEnd Channel.........: ");
  print_dec2(channel_end_);
  simple_uart_putstring((const uint8_t *)"\r\nTime on each channel: ");
  print_dec2(delayms_);
  simple_uart_putstring((const uint8_t *)" ms\r\n");
}

/**
 * main function
 * @return 0. int return type required by ANSI/ISO standard. 
 */
int main(void)
{ 
  radio_tests_t test = RADIO_TEST_NOP;
  radio_tests_t cur_test = RADIO_TEST_NOP;

  init();
  simple_uart_config(0, TX_PIN_NUMBER, 0, RX_PIN_NUMBER, false);  // Hardware flow control not used in this example
  simple_uart_putstring((const uint8_t *)"RF Test\r\n");
  NVIC_EnableIRQ(TIMER0_IRQn);
  __enable_irq();
  while(true)
  {
    switch(simple_uart_get())
    {
      case 'a':
        while(true)
        {
          simple_uart_putstring((const uint8_t *)"Enter start channel (two decimal digits, 00 to 80):");
          channel_start_ = get_dec2();
          if (channel_start_ <= 80)
            break;
          simple_uart_putstring((const uint8_t *)"Channel must be between 0 and 80\r\n");
        }
        test = cur_test;
        break;

      case 'b':
        while(true)
        {
          simple_uart_putstring((const uint8_t *)"Enter end channel (two decimal digits, 00 to 80):");
          channel_end_ = get_dec2();
          if (channel_end_ <= 80)
            break;
          simple_uart_putstring((const uint8_t *)"Channel must be between 0 and 80\r\n");
        }
        test = cur_test;
        break;

      case 'c':
        test = RADIO_TEST_TXCC;
        break;

      case 'd':
        while(true)
        {
          simple_uart_putstring((const uint8_t *)"Enter delay in ms (two decimal digits, 01 to 99):");
          delayms_ = get_dec2();
          if ((delayms_ > 0) && (delayms_ < 100))
            break;
          simple_uart_putstring((const uint8_t *)"Delay must be between 1 and 99\r\n");
        }
        test = cur_test;
        break;

      case 'e':
        radio_sweep_end();
        cur_test = RADIO_TEST_NOP;
        break;

      case 'm':
         get_datarate();
         test = cur_test;
         break;

      case 'o':
        test = RADIO_TEST_TXMC;
        simple_uart_putstring((const uint8_t *)"TX modulated carrier\r\n");
        break;

      case 'p':
        get_power();
        test = cur_test;
        break;

      case 'r':
        test = RADIO_TEST_RXSWEEP;
        simple_uart_putstring((const uint8_t *)"RX Sweep\r\n");
        break;

      case 's':
        print_parameters();
        break;

      case 't':
        test = RADIO_TEST_TXSWEEP;
        simple_uart_putstring((const uint8_t *)"TX Sweep\r\n");
        break;

      case 'x':
        test = RADIO_TEST_RXC;
        simple_uart_putstring((const uint8_t *)"RX constant carrier\r\n");
        break;

      case 'h':
      default:
        help();
        break;
    }
    switch(test)
    {
      case RADIO_TEST_TXCC:
        if (sweep)
        {
          radio_sweep_end();
          sweep = false;
        }
        radio_tx_carrier(txpower_, mode_, channel_start_);
        cur_test = test;
        test = RADIO_TEST_NOP;
        break;

      case RADIO_TEST_TXMC:
        if (sweep)
        {
          radio_sweep_end();
          sweep = false;
        }
        radio_modulated_tx_carrier(txpower_, mode_, channel_start_);
        cur_test = test;
        test = RADIO_TEST_NOP;
        break;

      case RADIO_TEST_TXSWEEP:
        radio_tx_sweep_start(txpower_, mode_, channel_start_, channel_end_, delayms_);
        sweep = true;
        cur_test = test;
        test = RADIO_TEST_NOP;
        break;

      case RADIO_TEST_RXC:
        if (sweep)
        {
          radio_sweep_end();
          sweep = false;
        }
        radio_rx_carrier(mode_, channel_start_);
        cur_test = test;
        test = RADIO_TEST_NOP;
        break;

      case RADIO_TEST_RXSWEEP:
        radio_rx_sweep_start(mode_, channel_start_, channel_end_, delayms_);
        sweep = true;
        cur_test = test;
        test = RADIO_TEST_NOP;
        break;

      case RADIO_TEST_NOP:
      default:
        break;
    }
  }
}

/**
 *@}
 **/
